package com.talent.web.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.google.common.collect.Sets;
import com.talent.web.config.minio.FileChunkVO;
import com.talent.web.config.minio.TlFileChunkDTO;
import com.talent.web.config.minio.TlMinioClient;
import com.talent.web.config.minio.TlMinioProperties;
import com.talent.web.enums.ResponseEnum;
import com.talent.web.exception.TalentException;
import com.talent.web.minio.MinioTemplate;
import com.talent.web.model.entity.TlFile;
import com.talent.web.model.vo.TlFileVo;
import com.talent.web.service.ITlFileService;
import com.talent.web.service.IUploadService;
import io.minio.ListPartsResponse;
import io.minio.messages.Part;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Nullable;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @ClassName：UploadServiceImpl
 * @Author: hszhou
 * @Date: 2025/3/29 14:45
 * @Description: 必须描述类做什么事情, 实现什么功能
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class UploadServiceImpl implements IUploadService {




    @Nullable
    private final TlMinioClient tlMinioClient;

    private final TlMinioProperties minioProperties;

    private final ITlFileService fileService;

    private final @Nullable MinioTemplate minioTemplate;


    @Override
    public FileChunkVO checkChunkExist(TlFileChunkDTO chunkDTO) {
        String identifier = chunkDTO.getIdentifier();
        String fileName = chunkDTO.getFilename();
        //总大小
        Long fileSize = chunkDTO.getTotalSize();
        Long chunkSize = chunkDTO.getChunkSize();
        //总分片
        Integer chunkNum = chunkDTO.getTotalChunks();
        String bucketName = getBucketName();
        String objectName = DateUtil.today() + "/" + IdUtil.randomUUID() + "." + fileName.substring(fileName.lastIndexOf(".") + 1);
        //已上传的列表
        Set<Integer> uploaded = Sets.newConcurrentHashSet();
        boolean skipUpload = false;
        //根据文件唯一标识md5判断之前是否上传过此文件
        log.info("根据文件唯一标识md5判断之前是否上传过此文件【{}】",identifier);
        TlFile file = fileService.selectFileByIdentifier(identifier);
        if (ObjectUtil.isNull(file)) {
            //

            String fileContentType = getFileContentType(fileName);
            log.info("初始胡的【{}】",fileContentType);
            String uploadId = tlMinioClient.getUploadId(bucketName, objectName,fileContentType);
            log.info("文件未上传 创建任务进行上传，minio的上传ID【{}】",uploadId);
            //存储数据库
            file = TlFile.builder()
                    .uploadId(uploadId)
                    .identifier(identifier)
                    .fileName(fileName)
                    .fileExtension(FileUtil.getSuffix(fileName))
                    .bucketName(bucketName)
                    .objectName(objectName)
                    .fileSize(fileSize)
                    .chunkSize(chunkSize)
                    .chunkNum(chunkNum)
          //          .createBy(SecurityUtils.getUsername())
                    .createTime(LocalDateTime.now())
                    .build();
            fileService.save(file);

            log.info("保存文件");
        } else {
            if (file.isFinish()) {
                log.info("文件已经上传了");
                skipUpload = true;
            } else {
                log.info("断点续传");
                boolean isExist = tlMinioClient.doesObjectExist(bucketName, objectName);
                log.info("文件是否存在【{}】",isExist);
                if (!isExist) {
                    ListPartsResponse listPartsResponse = tlMinioClient.
                            listMultipart(bucketName, file.getObjectName(), file.getUploadId());
                    uploaded = listPartsResponse.result().partList().stream().map(Part::partNumber).collect(Collectors.toSet());
                    log.info("已经存在了分片【{}】",uploaded.size());
                }
            }
        }
        return FileChunkVO.builder()
                .skipUpload(skipUpload)
                .uploaded(uploaded)
                .objectName(objectName)
                .uploadId(file.getUploadId())
                .build();
    }

    @Override
    public void uploadChunk(TlFileChunkDTO chunkDTO) {
        try (InputStream inputStream = chunkDTO.getFile().getInputStream()) {
            MultipartFile file = chunkDTO.getFile();
            String fileContentType = getFileContentType(file.getOriginalFilename());
            log.info("开始上传第【{}】快",chunkDTO.getChunkNumber());
            tlMinioClient.UploadPart(getBucketName(), chunkDTO.getObjectName(), inputStream, chunkDTO.getCurrentChunkSize(), chunkDTO.getUploadId(), chunkDTO.getChunkNumber(),fileContentType);
        } catch (Exception e) {
            log.error("上传分块失败", e);
            throw new RuntimeException("上传分块失败", e);
        }
    }

    @Override
    public boolean merge(String identifier) {
        log.info("开始合并【{}】的文件", identifier);
        TlFile file = fileService.selectFileByIdentifier(identifier);
        try {
            String bucketName = file.getBucketName();
            String uploadId = file.getUploadId();
            String objectName = file.getObjectName();
            //查询分片
            ListPartsResponse partResult = tlMinioClient.listMultipart(bucketName, objectName, uploadId);
            List<Part> partList = partResult.result().partList();
            //组装分片
            Part[] parts = partList.stream()
                    .map(part -> new Part(part.partNumber(), part.etag()))
                    .toArray(Part[]::new);
            String fileContentType = getFileContentType(file.getFileName());
            tlMinioClient.completeMultipartUpload(bucketName, objectName, uploadId, parts,fileContentType);
            //更新文件状态
            file.setIsFinish("1");
            file.setUpdateTime(LocalDateTime.now());
            fileService.updateById(file);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("合并文件{}失败", file);
            throw new TalentException(ResponseEnum.MERGE_FAIL);
        }
    }

    @Override
    public TlFileVo preview(String fileId) {
        TlFileVo vo = new TlFileVo();
        TlFile file = fileService.getById(fileId);
        if(ObjectUtil.isEmpty(file)){
           throw new TalentException(ResponseEnum.FILE_NOT_EXIT);
        }

        vo.setFileExtension(file.getFileExtension());
        vo.setFileName(file.getFileName());
        String preview = tlMinioClient.getPresignedObjectUrl(file.getBucketName(), file.getObjectName(), null);
        vo.setPreviewUrl(preview);

        return vo;
    }

    @Override
    public void dowload(String fileId, HttpServletResponse response) {
        TlFile file = fileService.getById(fileId);
        if(ObjectUtil.isEmpty(file)){
            throw new TalentException(ResponseEnum.FILE_NOT_EXIT);
        }

        String fileName = file.getFileName();
        String objectName = file.getObjectName();

        minioTemplate.download(null,objectName,response,fileName);

    }

    /**
     * 获取桶名
     *
     * @return
     */
    private String getBucketName() {
        return minioProperties.getBucket();
    }

    private static String getFileContentType(String fileName){
        String returnFileName = fileName.substring(fileName.lastIndexOf("."));
        if (returnFileName.equals(".jpeg") || returnFileName.equals(".png") || returnFileName.equals(".jpg")) {
            return "image/jpeg";
        } else if (returnFileName.equals(".mp4")) {
            return "video/mp4";
        } else if (returnFileName.equals(".html")) {
            return "text/html";
        } else if (returnFileName.equals(".css")) {
            return "text/css";
        } else if (returnFileName.equals(".js")) {
            return "application/javascript";
        } else if (returnFileName.equals(".pdf")) {
            return "application/pdf";
        } else if(returnFileName.equals(".doc")){
           return  "application/msword";
        } else  if(returnFileName.equals(".docx")){
            return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
        } else  if(returnFileName.equals(".xls")){
            return "application/vnd.ms-excel application/x-excel";
        } else  if(returnFileName.equals(".xlsx")){
            return " application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
        }else {
            return "application/octet-stream";
        }
    }
}
